/** @file    AutomationMessage.cpp
 *  @author  Alessandro Polo
 *  @version $Id: AutomationMessage.cpp 2057 2010-03-08 21:32:15Z alex $
 *  @brief
 * File containing methods for the wosh::automations::AutomationMessage class.
 * The header for this class can be found in AutomationMessage.h, check that file
 * for class description.
 ****************************************************************************/
/* Copyright (c) 2007-2010, WOSH - Wide Open Smart Home 
 * by Alessandro Polo - OpenSmartHome.com
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the OpenSmartHome.com WOSH nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY Alessandro Polo ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL Alessandro Polo BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ****************************************************************************/
 
 #include <framework/automations/AutomationMessage.h>

 #include <core/MethodsDef.h>


namespace wosh {
 namespace automations {

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////// CONSTRUCTORS

AutomationMessage::AutomationMessage() : Automation()
 {
	Automation::setName("AutomationMessage", this->uid);
 }

AutomationMessage::AutomationMessage( const AutomationMessage& m ) : Automation(m)
 {
	*this = m;
 }

AutomationMessage::~AutomationMessage()
 {
	Log(LOG_VERBOSE, ":~AutomationMessage() : Destroying.." );

	this->triggers.clear();
	this->effects.clear();
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

WRESULT AutomationMessage::execute( const Message* message, int64 )
 {
	this->effects.transactionBeginRead();
	WRESULT ret = WRET_OK;
	WRESULT ret_t = 0;
	int e = 0;

	tMessageList::ConstIterator it;
	const tMessageList::ConstIterator it_end = this->effects.end();
	for ( it=this->effects.begin(); it!=it_end; ++it ) {
		if ( *it == NULL ) continue;
		Message* effect = (*it)->clone();
		replaceVariables(effect, message);
		ret_t = postMessage(effect);
		if ( WSUCCEEDED(ret_t) )
			++e;
		else
			Log(LOG_WARNING, ":execute() :FAILED injecting Message" );
		ret += ret_t;
	 }

	if ( e != (int)this->effects.size() )
		ret = WRET_ERR_INTERNAL;

	if ( WSUCCEEDED(ret) ) {
		setLastExecutedTime();
		Log(LOG_INFO, ":execute() Injected %i Message(s)", e );
	 }
	else {
		Log(LOG_CRITICAL, ":execute() FAILED, Injected %i Message(s) of %i, T-Ret#%i", e, this->effects.size(), ret );
	 }
	this->effects.transactionEnd();
	return ret;
 }

bool AutomationMessage::isTrigger( const Message* message, int64 ) const
 {
	if ( message == NULL ) return false;
	MutexLockerRead mL(this->triggers.getMutex());
	tMessageFilterList::ConstIterator it_tr;
	const tMessageFilterList::ConstIterator it_tr_end = this->triggers.end();
	for ( it_tr=this->triggers.begin(); it_tr!=it_tr_end; ++it_tr ) {
		if ( *it_tr == NULL ) continue;
		short trig = (*it_tr)->compare(*message);
		if ( trig == 0 ) {
			return true;
		 }
	 }
	return false;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

WRESULT AutomationMessage::addTigger_( MessageFilter* trigger )
 {
	replaceVariables(trigger, NULL);
	this->triggers.push_back(trigger);
	return WRET_OK;
 }

WRESULT AutomationMessage::addEffect_( Message* effect )
 {
	this->effects.push_back(effect);
	return WRET_OK;
 }

WRESULT AutomationMessage::addTigger( MessageFilter* trigger )
 {
	MutexLockerWrite mL(this->triggers.getMutex());
	return addTigger_(trigger);
 }

WRESULT AutomationMessage::addEffect( Message* effect )
 {
	MutexLockerWrite mL(this->effects.getMutex());
	return addEffect_(effect);
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

WRESULT AutomationMessage::replaceVariables( Message* effect, const Message* trigger )
 {
	if ( effect == NULL ) return WRET_ERR_PARAM;

	std::string dest = effect->getDestination().toString();
	if ( replaceVariable( dest, trigger ) )
		effect->getDestination().fromString(dest);

	if ( !effect->isEmpty() ) {
		if ( effect->getContent()->isRequest() ) {
			Request* request = dynamic_cast<Request*>(effect->getContent());
// BUG, DEPRECATED BUG
//			if ( request->getData()->isClass(_Variant_TYPE) ) {
//				Variant* data = dynamic_cast<Variant*>(request->getData());
//				replaceVariable(data->asString(), trigger);
//			 }

		 }
	 }

	return WRET_OK;
 }

bool AutomationMessage::replaceVariable( std::string& var, const Message* trigger )
 {
	size_t pos1 = var.find("$");
	if ( pos1 == std::string::npos )
		return false;
	size_t pos2 = var.find("$", pos1+1);
	if ( pos2 == std::string::npos )
		return false;
	std::string field = var.substr(pos1+1, pos2-pos1-1);

	if ( trigger != NULL ) {
		if ( field == "SOURCE_NAME" ) {
			var.replace( pos1, field.size()+2, trigger->getSource().getName() );
			return true;
		 }
		else if ( field == "DESTINATION_NAME" ) {
			var.replace( pos1, field.size()+2, trigger->getDestination().getName() );
			return true;
		 }
	 }

	Variant data = Properties.getValueOf(field);
	if ( !data.isEmpty() )
		var.replace( pos1, field.size()+2, data.toString() );
	return true;
	return false;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

AutomationMessage& AutomationMessage::operator=(const AutomationMessage& m)
{
	if (this == &m) return *this; // same object?

	(Automation&)*this = (const Automation&)m;


	this->triggers.transactionBeginWrite();
	m.triggers.transactionBeginRead();
	this->effects.transactionBeginWrite();
	m.effects.transactionBeginRead();

	this->triggers.free();
	this->effects.free();

	tMessageFilterList::ConstIterator it_tr;
	const tMessageFilterList::ConstIterator it_tr_end = m.triggers.end();
	for ( it_tr=m.triggers.begin(); it_tr!=it_tr_end; ++it_tr ) {
		if ( *it_tr == NULL ) continue;
		addTigger_(*it_tr);
	 }

	tMessageList::ConstIterator it_ef;
	const tMessageList::ConstIterator it_ef_end = m.effects.end();
	for ( it_ef=m.effects.begin(); it_ef!=it_ef_end; ++it_ef ) {
		if ( *it_ef == NULL ) continue;
		addEffect_(*it_ef);
	 }

	m.effects.transactionEnd();
	this->effects.transactionEnd();
	m.triggers.transactionEnd();
	this->triggers.transactionEnd();

	return *this;
 }
 
bool AutomationMessage::operator==(const AutomationMessage &other) const
 {
	if ( (Automation&)*this != (const Automation&)other )
		return false;

	return true;
 }

//////////////////////////////////////////////////////////////////////////////////////////////// DEBUG & INFO
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////// SERIALIZATION

std::string AutomationMessage::toString() const
 {
	return "";
 }

WRESULT AutomationMessage::store( IEncoderWriter* serializer ) const {
	if ( serializer == NULL ) return WRET_ERR_PARAM;
	WRESULT ret = WRET_OK;
	return ret;
 }
WRESULT AutomationMessage::retrieve( IEncoderReader* deserializer ) {
	if ( deserializer == NULL ) return WRET_ERR_PARAM;
	WRESULT ret = WRET_OK;
	return ret;
 }

/////////////////////////////////////////////////////////////////////////////////////////////// SERIALIZATION
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

 }; // namespace automations
}; // namespace wosh
